package com.jlt.baidu.service.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.commons.codec.binary.Base64;
import org.opencv.core.Mat;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jlt.baidu.constant.BaiduConstant;
import com.jlt.baidu.constant.BaiduOcrErrorEnum;
import com.jlt.baidu.constant.OcrSupportType;
import com.jlt.baidu.dto.BookBusinessModel;
import com.jlt.baidu.dto.CustomExcelResponse;
import com.jlt.baidu.dto.CustomExcelResponse.ExcelItem;
import com.jlt.baidu.dto.CustomExcelResponse.ExcelRow;
import com.jlt.baidu.dto.CustomOcrResponse;
import com.jlt.baidu.dto.CustomOcrResponse.Detail;
import com.jlt.baidu.dto.FileOcrModel;
import com.jlt.baidu.dto.ReceiptBusinessModel;
import com.jlt.baidu.service.BaiduOcrService;
import com.jlt.baidu.utils.BaiduOCRUtil;
import com.jlt.baidu.utils.Opencv420Util;
import com.jlt.baidu.utils.OpencvRotateUtil;
import com.jlt.baidu.utils.PdfConvertImgaeUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 结合业务落地的ocr,非实时业务
 * 
 * @author 苹果
 * @date 2020/01/06
 */
@Slf4j
@Service
public class BaiduOcrServiceImpl implements BaiduOcrService {
    @Override
    public FileOcrModel disBookForUnderline(FileOcrModel model) {
        if (model == null) {
            return model;
        }
        try {
            if (!adaptatieFileType(model.getFileType(), model)) {
                return model;
            }
            long startTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            // pdf文件转成图片
            List<BufferedImage> images = PdfConvertImgaeUtil.pdf2png(model.getFileSaveUrl());
            BookBusinessModel resultModel = pdfOcrHandler(images, model);
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            model.setRequestTime((endTime - startTime) / 1000.0);
            model.setSuccessResult(JSON.toJSONString(resultModel));
        } catch (Exception e) {
            log.error("pdf预约单识别错误", e);
            // 错误对象
            model.setDiscernResult(1);
            model.setRepeatTime(1);
            model.setFailReasons("预约单识别系统错误500");
        }
        return model;
    }

    @Override
    public List<FileOcrModel> disBookForUnderline(List<FileOcrModel> models) {
        if (models == null || models.isEmpty()) {
            return models;
        }
        for (FileOcrModel model : models) {
            try {
                if (!adaptatieFileType(model.getFileType(), model)) {
                    continue;
                }
                long startTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                // pdf文件转成图片
                List<BufferedImage> images = PdfConvertImgaeUtil.pdf2png(model.getFileSaveUrl());
                BookBusinessModel resultModel = pdfOcrHandler(images, model);
                long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                model.setRequestTime((endTime - startTime) / 1000.0);
                model.setSuccessResult(JSON.toJSONString(resultModel));
            } catch (Exception e) {
                log.error("pdf预约单识别错误", e);
                // 错误对象
                model.setDiscernResult(1);
                model.setRepeatTime(1);
                model.setFailReasons("预约单识别系统错误500");
            }
        }
        return models;
    }

    /**
     * 
     * pdf进行ocr识别
     * 
     * @param images
     * @param model
     * @return
     * @throws IOException
     */
    public BookBusinessModel pdfOcrHandler(List<BufferedImage> images, FileOcrModel model) throws IOException {
        Double size = 0D;
        BookBusinessModel resultModel = new BookBusinessModel();
        String base64Image = imageProcess(images.get(0), size);
        CustomOcrResponse response = BaiduOCRUtil.ocrImageTemplate(base64Image, "a1f9c4066d406ac6bc5b1b3d0f8f4e8a");
        resultModel.setHeadLogId(response.getData().getLogId());
        for (Detail tmp : response.getData().getRet()) {
            if (tmp.getWord_name().equals("预约日期")) {
                resultModel.setBookDate(tmp.getWord());
            }
            if (tmp.getWord_name().equals("单据编号")) {
                resultModel.setOrderNo(tmp.getWord());
            }
            if (tmp.getWord_name().equals("预约波次")) {
                resultModel.setBookWave(tmp.getWord());
            }
            if (tmp.getWord_name().indexOf("物流宝单号") > 0 && tmp.getWord().indexOf("LBX") > -1) {
                if (resultModel.getLogisticsNo() == null) {
                    List<String> nos = new ArrayList<>();
                    nos.add(tmp.getWord());
                    resultModel.setLogisticsNo(nos);
                } else {
                    resultModel.getLogisticsNo().add(tmp.getWord());
                }
            }
        }
        StringBuilder bodyId = new StringBuilder();
        for (int i = 1; i < images.size(); i++) {
            String image1 = imageProcess(images.get(i), size);
            CustomExcelResponse response1 = BaiduOCRUtil.ocrImageExcel(image1);
            bodyId.append(response1.getLog_id()).append(",");
            // 固定取第一列的数据
            ExcelItem tmpItem = response1.getForms_result()[0];
            for (ExcelRow tmp : tmpItem.getBody()) {
                if (tmp.getColumn() == 1 && tmp.getWords().indexOf("LBX") > -1) {
                    resultModel.getLogisticsNo().add(tmp.getWords());
                }
            }
        }
        resultModel.setBodyLogId(bodyId.substring(0, bodyId.length() - 1));
        model.setFileCompareSize(size + "KB");
        return resultModel;
    }

    @Override
    public List<FileOcrModel> disReceiptForUnderline(List<FileOcrModel> models) {
        if (models == null || models.isEmpty()) {
            return models;
        }
        try {
            for (FileOcrModel model : models) {
                if (!adaptatieFileType(model.getFileType(), model)) {
                    continue;
                }
                long startTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                String base64Image = imageProcess(model.getFileSaveUrl(), model);
                CustomOcrResponse response = BaiduOCRUtil.ocrImageClassifier(base64Image, BaiduConstant.OCR_CLASS_RECEIPT);
                long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                model.setRequestTime((endTime - startTime) / 1000.0);
                requestResultHandler(model, response);
            }
            return models;
        } catch (Exception e) {
        }
        return models;
    }

    @Override
    public FileOcrModel disReceiptForUnderline(FileOcrModel model) {
        try {
            // 假定入口所有文件
            List<FileOcrModel> successList = new ArrayList<FileOcrModel>();
            List<FileOcrModel> allList = new ArrayList<FileOcrModel>();
            // 按照设想，业务和OCR分开，代码放在文件服务器，直接读取服务上文件，不从http远程调用
            // ocr识别成功后，主动去调用业务远程接口,用于定时器
            if (!adaptatieFileType(model.getFileType(), model)) {
                return model;
            }

            long startTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            String base64Image = imageProcess(model.getFileSaveUrl(), model);
            CustomOcrResponse response = BaiduOCRUtil.ocrImageClassifier(base64Image, BaiduConstant.OCR_CLASS_RECEIPT);
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();

            model.setRequestTime((endTime - startTime) / 1000.0);
            requestResultHandler(model, response);
            return model;
        } catch (Exception e) {
            log.error("回单OCR识别业务异常：", e);
        }
        return model;
    }

    /**
     * 判断附件类型,如果是图片对图片进行格式转化，如果是pdf，对pdf进行格式转化，除此之外其他进行类型判断错误
     * 
     * @param type
     * @param model
     */
    private boolean adaptatieFileType(String type, FileOcrModel model) {
        boolean isImage = OcrSupportType.IMAGE_JPEG.equalsIgnoreCase(type) || OcrSupportType.IMAGE_JPG.equalsIgnoreCase(type)
                || OcrSupportType.IMAGE_PNG.equalsIgnoreCase(type) || OcrSupportType.IMAGE_BMP.equalsIgnoreCase(type);
        boolean isPdf = OcrSupportType.FILE_PDF.equalsIgnoreCase(type);
        if (!isImage && !isPdf) {
            // 错误对象
            model.setDiscernResult(1);
            model.setRepeatTime(1);
            model.setFailReasons(BaiduOcrErrorEnum.ERROR_10086.getName());
            return false;
        }
        return true;
    }

    /**
     * 图片处理，进行压缩，旋转，转成base64，opencv路径不能有中文
     * 
     * @param filaPath
     * @param type
     * @return
     */
    public String imageProcess(String filePath, FileOcrModel model) {
        Mat source = Opencv420Util.imagerLoad(filePath);
        // 判断方向旋转
        Mat data = OpencvRotateUtil.handleMatRange(source);
        // 判断大小压缩
        int[] size = Opencv420Util.ocrImageZoomRegex(data);
        Mat result = Opencv420Util.imageZoomForOCR(data, size[0], size[1]);
        // 转成base64对象
        byte[] bytes = Opencv420Util.matToByte(result, model.getFileType());
        model.setFileCompareSize(bytes.length / 1024 + "KB");
        // mat必须释放，减少OOM
        source.release();
        data.release();
        result.release();
        return new String(Base64.encodeBase64(bytes));
    }

    /**
     * 图片处理，进行压缩，旋转，转成base64
     * 
     * @param images
     * @param model
     * @return
     * @throws IOException
     */
    public String imageProcess(BufferedImage image, Double size) throws IOException {
        // 转成base64对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream();// io流
        ImageIO.write(image, "png", baos);// 写入流中
        byte[] bytes = baos.toByteArray();// 转换成字节
        size += bytes.length / 1024;
        return new String(Base64.encodeBase64(bytes));
    }

    /**
     * 对百度返回结果进行分析
     * 
     * @param model
     * @param response
     */
    private void requestResultHandler(FileOcrModel model, CustomOcrResponse response) {
        ReceiptBusinessModel result = new ReceiptBusinessModel();
        result.setLogId(response.getData().getLogId());
        result.setTemplateName(response.getData().getTemplateName());
        if (BaiduOcrErrorEnum.ERROR_0.getCode().equals(response.getError_code())) {
            // 对天猫计划采购单进行补全 识别LBX单号少了一个X
            Detail[] dtails = response.getData().getRet();
            for (Detail detail : dtails) {
                if ("LBX单号".equals(detail.getWord_name())) {
                    result.setLogisticsNo(detail.getWord());
                }
                if ("计划采购单".equals(response.getData().getTemplateName())) {
                    result.setLogisticsNo(detail.getWord().replace("LB0", "LBX0"));
                }
                if ("业务类型".equals(detail.getWord_name())) {
                    result.setBusinessType(detail.getWord());
                }
                if ("收货仓库".equals(detail.getWord_name())) {
                    result.setWarehouseName(detail.getWord());
                }
                if ("预约时间".equals(detail.getWord_name())) {
                    // 2019-12-1508:00-11:30 -> 2019-12-15 08:00-11:30
                    String time = detail.getWord();
                    result.setPrediectTime(time);
                }
            }
            // 成功
            model.setDiscernResult(0);
            model.setSuccessResult(JSONObject.toJSONString(result));
        } else {
            // 失败
            model.setDiscernResult(1);
            model.setRepeatTime(1);
            model.setFailReasons(BaiduOcrErrorEnum.getErrorMessage(response.getError_code()));
        }
    }
}
